home *** CD-ROM | disk | FTP | other *** search
- /*
- HASMenuWindows.c from Hsoi's App Shell. © 1995-1997 John C. Daub. All rights reserved.
-
- This file contains things pertaining to our "Windows" menu, such as stacking, tiling,
- and things to maintain a running list of our open windows. The code to maintain
- the menu itself (enable/disable, etc) is in the regular menus file.
-
- This code is based upon code from Scott Knaster and Keith Rollin's book,
- "Macintosh Programming Secrets" 2nd ed. from Chapter 7 which dealt with
- windows. I've modified the code to work with Hsoi's App Shell. These modifictions
- include (but are not limited to): converting variables from "pre-Copland" to
- Copland (e.g. WindowPtr to WindowRef); using !OLDROUTINENAMES; using pre-Copland
- accessor routines for ease of migration to Copland; writing and using
- UniversalProcPtr's for function callbacks; any other necessary modifications
- to allow both 68k and PPC native code; routine name changes for consistancy
- with HAS (e.g. HsoiXXX()); any other code changes (removal, modification, etc.)
- to function consistantly with HAS (e.g. a Windows menu adjuster routine); etc.
-
- One big change worthy of further explanation: in the original Knaster/Rollin's
- code, they maintained their window list by having each window's refCon field point
- to the youngest/next window. In HAS, we already use the window's refCon field
- to store a reference to the DocumentRecord associated with that window. So
- to expand upon this, I've added a "nextWindow" member to the DocumentRecord
- which takes the place of this. to access the next window, we access the
- current window's refCon to get the DocumentRecord, then get the next window
- from there. I wrote a couple functions to facilitate the setting and getting
- of this reference.
-
- */
-
- #pragma mark ••• #includes •••
-
- #ifndef _WASTE_
- #include "WASTE.h"
- #endif
- #include "HASGlobals.h"
- #ifndef __HSOIS_APP_SHELL__
- #include "HASMain.h"
- #endif
- #include "HASMenuWindows.h"
- #include "HASWindows.h"
- #include "HASUtilities.h"
-
- #pragma mark -
- #pragma mark ••• Constants •••
-
- const short kOffscreenLocation = 0x4000; // Halfway to infinity
-
- #pragma mark -
- #pragma mark ••• Globals •••
-
- // the following variables are only needed for functions "interal" to this file.
- // therefore, we'll just declare these "local-globals" here instead of in our main
- // globals file.
-
- short pMaxWindowsPerRow = 0;
- short pMaxWindowsPerColumn = 0;
- short pNumberOfColumns = 0;
- short pNumberOfRows = 0;
-
- // Used when matching up windows to their menu items, and vice-versa.
-
- short gMenuItem = 0;
- Boolean gDoneCounting = 0;
- WindowRef gFoundWindow = nil;
- WindowRef gTargetWindow = nil;
-
-
- // Variables set up by HsoiForEachWindowDo() and HsoiForEachWindowPerScreenDo()
-
- short gWindowNumber = 0; // Ordinal number of the current window on the current monitor.
-
- short gNumberOfWindows = 0; // Total number of windows on the current monitor.
-
- GDHandle gScreenDevice= nil; // Reference to the current monitor.
-
- Rect gScreenRect = {0,0,0,0}; // Bounds of the current monitor. Takes into account the menubar
- // if this is the main monitor.
-
-
- // Variables set up and shared by tiling and stacking routines.
-
- short gScreenWidth = 0; // Width of the current monitor.
-
- short gScreenHeight = 0; // Height of the current monitor.
-
- short gNewWindowWidth = 0; // When tiling or stacking, every window is essentially the same
- // size. This variable holds the window’s new width.
-
- short gNewWindowHeight = 0; // Ditto, but for height.
-
- // and our UPP's...
-
- HsoiSetUpUPP gTileSetupUPP = nil;
- HsoiWindowActionUPP gTileTheWindowUPP = nil;
- HsoiSetUpUPP gStackSetupUPP = nil;
- HsoiWindowActionUPP gStackTheWindowUPP = nil;
- HsoiWindowActionUPP gLookForSelectedWindowUPP = nil;
- HsoiWindowActionUPP gCountWindowsUPP = nil;
- HsoiWindowActionUPP gLookForPreviousWindowUPP = nil;
- HsoiWindowActionUPP gCountSomeWindowUPP = nil;
-
-
- #pragma mark -
- #pragma mark ••• Window Tiling •••
-
- // this is the function called by DoMenuCommand for Tiling windows
-
-
- /*******************************************************************************
-
- DoTileWindows
-
- Call ForEachWindowPerScreenDo, which is a routine that allows us to
- perform a specified operation on every window. Our task here is to tile
- all the windows, making sure that each is the same size, but in
- non-overlapping locations. In our TileSetup routine, we calculate the
- windows’ size and save it off. In our TileTheWindow routine, we set each
- window to that size, and position it in the right place.
-
- *******************************************************************************/
- void HsoiDoTileWindows( void )
- {
- HsoiForEachWindowPerScreenDo( gTileSetupUPP, gTileTheWindowUPP, nil );
-
- return;
- }
-
-
- //
- // This function is called once per monitor by ForEachWindowPerScreenDo.
- // Based on the size of the screen and the number of windows that are to
- // be tiled on the screen, we determine the number of rows and columns
- // that we want to tile the windows into. We also determine the size
- // each window on this screen should be.
- //
- void HsoiTileSetup( void )
- {
- if ( gNumberOfWindows > 0 )
- {
- // the calculations in here should be pretty self-explanitory
-
- gScreenRect.top += kGapBetweenWindows;
- gScreenRect.left += kGapBetweenWindows;
-
- gScreenWidth = gScreenRect.right - gScreenRect.left;
- gScreenHeight = gScreenRect.bottom - gScreenRect.top;
-
- pMaxWindowsPerRow = gScreenWidth / kMinWidth;
- pMaxWindowsPerColumn = gScreenHeight / kMinHeight;
-
- pNumberOfColumns = (gNumberOfWindows - 1) / pMaxWindowsPerColumn + 1;
- pNumberOfRows = (gNumberOfWindows - 1) / pNumberOfColumns + 1;
-
- gNewWindowWidth = gScreenWidth / pNumberOfColumns;
- gNewWindowHeight = gScreenHeight / pNumberOfRows;
- }
-
- return;
- }
-
- //
- // Main window cruncher for tiling. This routine is called once for every
- // window. Makes the given window the size we determined in TileSetUp, and
- // place it in the appropriate row and column based on its ordering in the
- // window list.
- //
- // Note that our calculations are set up to determine the size and location
- // of the windows _frame_, not its content rectangle (i.e., its portRect).
- // However, MoveWindow and SizeWindow like to work with values that _do_
- // affect the portRect. To solve this problem, we do the actual moving and
- // resizing by creating a routine called SetWindowBounds. This utility
- // routine translates the frame rectangle into the content rectangle for us
- // before calling MoveWindow and SizeWindow.
- //
- void HsoiTileTheWindow(WindowRef theWindow)
- {
- Rect newBounds;
- short row, column;
-
- // the calculations here should be pretty self-explanitory
-
- row = (gWindowNumber - 1) / pNumberOfColumns;
- column = (gWindowNumber - 1) % pNumberOfColumns;
-
- newBounds.left = gScreenRect.left + column * gNewWindowWidth;
- newBounds.top = gScreenRect.top + row * gNewWindowHeight;
- newBounds.right = newBounds.left + gNewWindowWidth - kGapBetweenWindows;
- newBounds.bottom = newBounds.top + gNewWindowHeight - kGapBetweenWindows;
-
- // set the window's bounds
-
- HsoiSetWindowBounds(theWindow, newBounds);
-
- // and readjust the window so it looks right
-
- HsoiReadjustAfterStackTile(theWindow );
-
- return;
- }
-
-
- #pragma mark -
- #pragma mark ••• Window Stacking •••
-
- // this is the function called by DoMenuCommand to stack the windows
-
- /*******************************************************************************
-
- DoStackWindows
-
- Call ForEachWindowPerScreenDo, which is a routine that allows us to
- perform a specified operation on every window. Our task here is to stack
- all the windows. In our StackSetup routine, we calculate the number of
- diagonals we’ll have on the monitor we are tiling on. We also calculate an
- initial window size, a size which gets smaller as subsequent windows get
- stacked further down and to the right. In our TileTheWindow routine, we
- set each window to the appropriate size and position.
-
- *******************************************************************************/
- void HsoiDoStackWindows( void )
- {
-
- HsoiForEachWindowPerScreenDo( gStackSetupUPP, gStackTheWindowUPP, nil );
-
- return;
- }
-
-
-
- //
- // This function is called once per monitor by ForEachWindowPerScreenDo.
- // Its purpose is two-fold. First, it figures out how many diagonals of
- // windows will be needed to stack all of the windows on the current screen.
- // Right now, we permit only 8 windows to be stacked in a diagonal before we
- // need to create a new diagonal of windows that’s move over to the right a
- // little bit.
- //
- // The second thing this function does is figure out a good initial size for
- // the stacked windows. This size shrinks for windows that are stacked
- // further down and to the right on the screen. The calculations for this
- // shrinking are in the StackTheWindow function.
- //
- void HsoiStackSetup( void )
- {
- short maxWindowsInDiagonal;
- short numberOfBottomWindows;
-
- InsetRect(&gScreenRect, kGapBetweenWindows, kGapBetweenWindows);
-
- // Find the height and width of this window
-
- gScreenWidth = gScreenRect.right - gScreenRect.left;
- gScreenHeight = gScreenRect.bottom - gScreenRect.top;
-
- // Find the longest diagonal of windows we will be dealing
- // with. This will either be kWindowsPerDiagonal or
- // gNumberOfWindows, whichever is smaller.
-
- maxWindowsInDiagonal = gNumberOfWindows;
- if (maxWindowsInDiagonal > kWindowsPerDiagonal)
- maxWindowsInDiagonal = kWindowsPerDiagonal;
-
- // Find out how many windows will end up in the last
- // position of a diagonal. This number is crucial in
- // determining the horizontal span of all the windows,
- // which is used in calculating the windows’ width.
-
- numberOfBottomWindows = (gNumberOfWindows / kWindowsPerDiagonal);
- if (numberOfBottomWindows > 0)
- --numberOfBottomWindows;
-
- // Figure out the size of the first window to be stacked.
- // We start off with the the size of the entire screen and
- // start trimming it back to allow for other windows. First,
- // we trim back the width by an amount equal to the number
- // of pixels that the rightmost window will be from the
- // left edge of the screen. Next, we trim back the height so
- // that we can accommodate the tallest diagonal we’ll be dealing
- // with.
-
- gNewWindowWidth = gScreenWidth
- - (((maxWindowsInDiagonal - 1) + numberOfBottomWindows)
- * kHorizontalStagger);
- gNewWindowHeight = gScreenHeight
- - (maxWindowsInDiagonal - 1) * kHorizontalStagger;
-
- return;
- }
-
-
- //
- // This routine is called once for each window. Its function is to calculate
- // the size and location for each window. First, it determines which diagonal
- // the specified window should be in and its position within that diagonal.
- // Then it determines the horizontal position for the window; as each window
- // is always moved to the right from the previous window by a constant
- // amount, the horizontal position is a simple function of the window’s
- // creation rank. Next, we determine the vertical position of the window.
- // This, too, is a simple function, this time of the window’s position in the
- // diagonal it will be placed in. What this means is that the 1st, 9th, 17th,
- // etc., windows will all have the same vertical position. Similarly for the
- // 2nd, 10th, 18th, etc., windows, and so on.
- //
- // Next, we have to determine the size of the window. First, we consider the
- // width. Each window in a diagonal is the same size. This size is determined
- // by taking the initial window size calculated in StackSetup, and
- // subtracting an amount that is a function of the diagonal number we are
- // working on. First, we subtract an amount such that the right edge of each
- // window in this diagonal will line up with the corresponding window in the
- // previous diagonal. Next, we add back 10 pixels just to give a nice
- // staggering effect on the right hand side as well as the left. After doing
- // that, we work on figuring the height of the window. We start off with the
- // standard height calculated in StackSetup. Then we subtract a multiple of
- // 10 pixels that is based on how far down in the diagonal the window is.
- // This means that the height of the window is a function of the window’s
- // position in the diagonal, while the width of the window is a function of
- // the position of the diagonal itself.
- //
- void HsoiStackTheWindow(WindowRef theWindow)
- {
- Rect newBounds;
- short placeInDiagonal, diagonal;
-
- placeInDiagonal = (gWindowNumber - 1) % kWindowsPerDiagonal;
- diagonal = (gWindowNumber - 1) / kWindowsPerDiagonal;
-
- newBounds.left = gScreenRect.left + (gWindowNumber - 1) * kHorizontalStagger;
- newBounds.top = gScreenRect.top + placeInDiagonal * kVerticalStagger;
- newBounds.right = newBounds.left + gNewWindowWidth - diagonal * (kWindowsPerDiagonal * kHorizontalStagger - 10);
- newBounds.bottom = newBounds.top + gNewWindowHeight - placeInDiagonal * 10;
-
- HsoiSetWindowBounds(theWindow, newBounds);
-
- HsoiReadjustAfterStackTile(theWindow);
-
- return;
- }
-
-
- #pragma mark -
- #pragma mark ••• Windows Menu Handlers •••
-
- // this is called by DoMenuCommand when a specific window is chosen fromthe menu
-
-
- /*******************************************************************************
-
- DoSelectFromWindowsMenu
-
- Called when the user selects one of the menu items containing a window’s
- name (in the Windows menu). We stash off the menu item that was selected,
- and then start iterating over all the windows in chronological order.
- Since the list of window names in the Windows menu is also maintained in
- chronological order, we know that there is a direct relationship between
- the menu item number and the window’s chronological rank. In other words,
- if we selected the Xth item in the Windows menu (not including the Tile or
- Stack menu items), all we have to do is find the Xth oldest window and
- select it.
-
- *******************************************************************************/
- void HsoiDoSelectFromWindowsMenu(short menuItem)
- {
- gMenuItem = menuItem - iFirstWindow + 1;
-
- HsoiForEachWindowDo( nil, gLookForSelectedWindowUPP, nil );
-
-
- return;
- }
-
-
- void HsoiLookForSelectedWindow(WindowRef theWindow)
- {
- if (--gMenuItem == 0)
- SelectWindow(theWindow);
-
- return;
- }
-
-
- /*******************************************************************************
-
- AddWindowToMenu
-
- Called to add a newly created window to the Windows menu. We make that
- addition by making a call to AppendMenu to create the new menu item, and
- then SetItem to set that item’s name. The reason why we don’t let
- AppendMenu set the name is because of the meta-character processing that
- AppendMenu performs. If our window’s name happened to have something like
- a “!” or “/” it it, what appeared in the menu wouldn’t be what we
- expected. SetItem doesn’t do recognize the meta-characters, so we avoid
- that problem when calling it.
-
- Next, we insert our window into our own private window list. Normally, you
- don’t need to do this. The Window Manager keeps track of all the windows
- on the screen so that you don’t have to. However, the list it manages
- links the windows in front to back order. In other words, the frontmost
- window is at the front of the list, the record for the window behind it is
- second in the list, and so on.
-
- In order to manage our Windows menu, we’d really like to have a list of
- windows in chronological order. We do this by using the refCon field of
- the window record. Each window’s refCon field will point to the next
- youngest window. The oldest window (the head of the chain) will be kept in
- the global variable gFirstWindow. The refCon field of the last window is
- nil. If there are no windows, gFirstWindow is nil.
-
- *******************************************************************************/
- void HsoiAddWindowToMenu(WindowRef theWindow)
- {
- Str255 title;
- MenuRef windowsMenu;
- WindowRef lastWindow;
-
- // get the title of the window to stick into the window's menu as the item
-
- GetWTitle(theWindow, title);
-
- // get a handle to the window's menu
-
- windowsMenu = GetMenu(mWindows);
-
- // stick something in there, but not our actual text just in case of meta characters
-
- AppendMenu(windowsMenu, "\pNeed something here or call doesn’t work.");
-
- // and now properly give the menu item the right text (again, meta characters)
-
- SetMenuItemText( windowsMenu, CountMenuItems(windowsMenu), title );
-
- // and now update our linked list of windows. add this one to the list
-
- lastWindow = HsoiGetPreviouslyCreatedWindow(nil); /* nil means “get last window” */
- if (lastWindow != nil)
- HsoiSetNextWindow( lastWindow, (long)theWindow );
- else
- gFirstWindow = theWindow;
-
- return;
- }
-
-
- /*******************************************************************************
-
- RemoveWindowFromMenu
-
- Get the menu item that corresponds to this window, and remove that menu
- item from the Windows menu. Remove the window from our chronological list
- of windows.
-
- *******************************************************************************/
- void HsoiRemoveWindowFromMenu(WindowRef theWindow)
- {
- MenuRef windowsMenu;
- WindowRef previousWindow;
-
- // get a handle to the windows menu
-
- windowsMenu = GetMenu(mWindows);
-
- // remove the menu item
-
- DeleteMenuItem(windowsMenu, HsoiGetMenuItemForWindow(theWindow));
-
- // and update our linked list of windows by removing this one from the list
-
- if (theWindow == gFirstWindow) {
- gFirstWindow = (WindowRef)HsoiGetNextWindow(theWindow);
- } else {
- previousWindow = HsoiGetPreviouslyCreatedWindow(theWindow);
- HsoiSetNextWindow( previousWindow, HsoiGetNextWindow(theWindow) );
- }
-
- HsoiSetNextWindow( theWindow, 0 );
-
- return;
- }
-
-
- /*******************************************************************************
-
- GetMenuItemForWindow
-
- Given a window pointer, return the number for the Windows menu item that
- corresponds to it. This is done by iterating over all our windows in
- chronological order, incrementing a counter as we go. When we get to the
- window we are interested in, we stop counting. This means that our counter
- ends up hold the chronological rank of our window. This rank is then added
- to iFirstWindow to give us the appropriate menu item number.
-
- *******************************************************************************/
-
- short HsoiGetMenuItemForWindow(WindowRef theWindow)
- {
- gDoneCounting = false;
- gTargetWindow = theWindow;
- gMenuItem = iFirstWindow - 1;
-
- HsoiForEachWindowDo( nil, gCountSomeWindowUPP, nil );
-
- return gMenuItem;
- }
-
-
- // this adjusts our Windows menu. this was not part of the original Knaster/Rollins
- // code.
-
- void HsoiAdjustWindowsMenu( WindowRef window )
- {
- short menuItem;
- short i;
- MenuRef windowsMenu = GetMenuHandle( mWindows );
-
- // first, remove all the check marks from the menu
-
- for ( i = 1; i<= CountMenuItems( windowsMenu ); i++ )
- CheckItem( windowsMenu, i, false );
-
- // if we have a NIL window, or a window that's not a document window (cause
- // this menu only tracks document windows), return...nothing else to do
-
- if ( !HsoiIsDocumentWindow( window ) )
- return;
-
- // find out which menu item is the one for our window
-
- menuItem = HsoiGetMenuItemForWindow( window );
-
- // and put a check mark by that window
-
- CheckItem( windowsMenu, menuItem, true );
-
- // now, set the item's menu command (this is sorta just "cosmetic")
-
- for ( i = iFirstWindow; i <= CountMenuItems(windowsMenu); i++ )
- {
- // we only want menu commands for the first 9 items (allows cmd-1 to cmd-9)
- if ( i >= (iFirstWindow + 9) )
- break;
-
- // and this sets the cmd...the funky math converts the counter to the ASCII
- // (decimal) equivs ( number 1 is ASCII 49 (decimal))
-
- SetItemCmd( windowsMenu, i, (char)(i - iFirstWindow + 1 + 48) );
- }
-
- return;
- }
-
- #pragma mark -
- #pragma mark ••• Window Iteraters •••
-
- /*******************************************************************************
-
- ForEachWindowDo
-
- Routine that iterates over all of the windows. It first counts up the
- number of windows on the monitor we are currently examining. Next, it
- calls a setup routine provided by the caller. After that, it calls an
- action procedure for each window. This action procedure is passed a
- pointer to the current window we are iterating over. It is also able to
- access the total number of windows and the window’s rank through the
- global variables gNumberOfWindows and gWindowNumber. After all windows
- have been acted upon, a clean up routine provided by the caller is called.
-
- Note that windows are iterated in chronological order, as maintained by
- the links stored in the their refCon fields.
-
- *******************************************************************************/
- void HsoiForEachWindowDo(HsoiSetUpUPP theStarter,
- HsoiWindowActionUPP theDoer,
- HsoiFinishUpUPP enderWiggin)
-
- {
- WindowRef currentWindow;
- Boolean windowIsOurConcern;
-
- // To get the number of windows, we recursively call ourself
- // with an action procedure that simply increments a counter.
- // So that we don’t infinitely recurse, we check the action
- // procedure to see if it’s our counting routine. If not,
- // we don’t recurse.
-
- if (theDoer != gCountWindowsUPP )
- {
- gNumberOfWindows = 0;
-
- HsoiForEachWindowDo( nil, gCountWindowsUPP, nil );
- }
-
- if ((theDoer == gCountWindowsUPP) || (gNumberOfWindows > 0)) {
-
- // Start keeping track of window rank
-
- gWindowNumber = 0;
-
- // If the caller provided a setup routine, call it.
-
- if (theStarter != nil)
- CallHsoiSetUpProc( theStarter );
-
- // Start iterating over all the windows. Skip over any DA’s or
- // dialog windows. The expression that sets “windowIsOurConcern”
- // could be modified to also skip over invisible windows.
-
- if (theDoer != nil) {
- currentWindow = gFirstWindow;
- while (currentWindow != nil) {
- windowIsOurConcern = HsoiIsDocumentWindow(currentWindow);
- if (windowIsOurConcern) {
- ++gWindowNumber;
- CallHsoiWindowActionProc( theDoer, currentWindow );
- }
- currentWindow = (WindowRef)HsoiGetNextWindow(currentWindow);
- }
- }
-
- // Done with all the windows. If the caller
- // provided a cleanup routine, call it.
-
- if (enderWiggin != nil)
- CallHsoiFinishUpProc( enderWiggin );
- }
-
- return;
- }
-
-
- /*******************************************************************************
-
- ForEachWindowPerScreenDo
-
- Ugly-ass routine that iterates over all of the windows, but in a peculiar
- fashion. What it does is first iterate over all the monitors hooked up to
- the machine. For each monitor, it first counts up the number of windows on
- the monitor we are currently examining. Next, it calls a setup routine
- provided by the caller. After that, it calls an action procedure for each
- window on the monitor. This action procedure is passed a pointer to the
- current window we are iterating over. It is also able to access the handle
- to the current monitor, the size of the current monitor, the number of
- windows on the current monitor, and the window’s rank on the current
- monitor through the global variables gScreenDevice, gScreenRect,
- gNumberOfWindows, and gWindowNumber. After all windows on the monitor have
- been acted upon, a clean up routine provided by the caller is called.
- Finally, we move on to the next monitor.
-
- Note that windows are iterated in chronological order, as maintained by
- the links stored in the their refCon fields.
-
- *******************************************************************************/
- void HsoiForEachWindowPerScreenDo(HsoiSetUpUPP theStarter,
- HsoiWindowActionUPP theDoer,
- HsoiFinishUpUPP enderWiggin)
-
- {
- WindowRef currentWindow;
- Boolean windowIsOurConcern;
- GDHandle currentScreenDevice;
-
- if (gHasColorQD)
- currentScreenDevice = GetDeviceList();
- else
- currentScreenDevice = nil;
-
- do {
-
- // To get the number of windows on the current monitor of
- // interest, we recursively call ourself with an action
- // procedure that simply increments a counter. So that we
- // don’t infinitely recurse, we check the action procedure
- // to see if it’s our counting routine. If not, we don’t recurse.
-
- if (theDoer != gCountWindowsUPP)
- {
- gNumberOfWindows = 0;
-
- HsoiForEachWindowPerScreenDo( nil, gCountWindowsUPP, nil );
- }
-
- if ((theDoer == gCountWindowsUPP) || (gNumberOfWindows > 0)) {
- // Start keeping track of window rank on this monitor
-
- gWindowNumber = 0;
-
- // Export the handle to the current device
-
- gScreenDevice = currentScreenDevice;
-
- // Export the size of the monitor. If we have a valid device
- // handle, use it to get the device’s size. If we don’t have
- // a valid handle (it is nil -- indicating that we are running
- // on a machine without Color QuickDraw), get the size of the
- // screen from GetMainScreenRect() (which will effectively
- // return qd.screenBits.bounds in this case).
-
- if (currentScreenDevice != nil) {
- gScreenRect = (*currentScreenDevice)->gdRect;
-
- //•• this doesn't make sense...shouldn't it be if current == GetMainDevice()?
- // that seems most logical to me...unfortunately, i can't test this too much
- // cause i don't have a multiple monitor setup.
-
- if (currentScreenDevice == GetMainDevice())
- // currentScreenDevice = GetMainDevice();
- // if ( currentScreenDevice )
- gScreenRect.top += LMGetMBarHeight();
- } else {
- gScreenRect = HsoiGetMainScreenRect();
- gScreenRect.top += LMGetMBarHeight();
- }
-
- // If the caller provided a setup routine, call it.
-
- if (theStarter != nil)
- CallHsoiSetUpProc( theStarter );
-
- // Start iterating over all the windows. If the majority of the
- // windows is on the monitor we are currently looking at from
- // our outer loop, pass that window to the caller’s action
- // procedure. Skip over any DA’s or dialog windows. The expression
- // that sets “windowIsOurConcern” could be modified to also skip
- // over invisible windows.
-
- if (theDoer != nil) {
- currentWindow = gFirstWindow;
- while (currentWindow != nil) {
- windowIsOurConcern = HsoiIsDocumentWindow(currentWindow) &&
- (currentScreenDevice == HsoiGetWindowDevice(currentWindow));
-
- if (windowIsOurConcern) {
- ++gWindowNumber;
- CallHsoiWindowActionProc( theDoer, currentWindow );
- }
- currentWindow = (WindowRef)HsoiGetNextWindow(currentWindow);
- }
- }
-
- // Done with all the windows on this monitor. If the caller
- // provided a cleanup routine, call it.
-
- if (enderWiggin != nil)
- CallHsoiFinishUpProc( enderWiggin );
- }
-
- // Do the next monitor
-
- if (gHasColorQD)
- currentScreenDevice = GetNextDevice(currentScreenDevice);
-
- } while (currentScreenDevice != nil);
-
- return;
- }
-
- #pragma mark -
- #pragma mark ••• Other Window Procs •••
-
- /*******************************************************************************
-
- CountWindows
-
- This is a WindowActionProc called by ForEachWindowPerScreenDo and
- ForEachWindowDo to count windows.
-
- *******************************************************************************/
- void HsoiCountWindows(WindowRef theWindow)
- {
- #pragma unused ( theWindow )
-
- ++gNumberOfWindows;
-
- return;
- }
-
-
- /*******************************************************************************
-
- GetPreviouslyCreatedWindow
-
- Called to return the window immediately before the target window in our
- chronological list of windows. This is handy for searching backwards when
- working with a singly linked list of records.
-
- *******************************************************************************/
- WindowRef HsoiGetPreviouslyCreatedWindow(WindowRef theWindow)
- {
- gFoundWindow = nil;
- gTargetWindow = theWindow;
- HsoiForEachWindowDo( nil, gLookForPreviousWindowUPP, nil );
-
- return gFoundWindow;
- }
-
- void HsoiLookForPreviousWindow(WindowRef theWindow)
- {
- if ( gTargetWindow == (WindowRef)HsoiGetNextWindow(theWindow))
- gFoundWindow = theWindow;
-
- return;
- }
-
-
- void HsoiCountSomeWindow(WindowRef theWindow)
- {
- if (!gDoneCounting) {
- ++gMenuItem;
- if (theWindow == gTargetWindow)
- gDoneCounting = true;
- }
-
- return;
- }
-
- #pragma mark -
- #pragma mark ••• Window Utilities •••
-
-
-
- /*******************************************************************************
-
- SetWindowBounds
-
- Set the size and location of the given window’s FRAME. Note that this
- differs from a simple MoveWindow/SizeWindow combination in that the
- parameters passed to those routines work on the window’s content
- rectangle, not the outer frame rectangle.
-
- What we do to accomplish this is to take the frame rectangle passed into
- this routine and calculate what the content rectangle would be for a
- window that had that frame’s size. This is done by looking at the
- window’s current strucRgn (the region that describes the window’s frame)
- and contRgn (the region that describes the inside content area of the
- window). The difference in size between these two regions is determined,
- and is then applied to “newBounds”. This gives us a rectangle that can be
- passed to MoveWindow and SizeWindow.
-
- Note that we first hide the window before changing its bounds. This is so
- that we don’t first see the effect of MoveWindow, and then the effect of
- SizeWindow.
-
- *******************************************************************************/
- void HsoiSetWindowBounds(WindowRef theWindow, Rect newBounds)
- {
- short top;
- short left;
- short height;
- short width;
-
- short topInset;
- short leftInset;
- short bottomInset;
- short rightInset;
-
- Rect oldBounds;
-
- // always make sure you initialize a Region before you use it!
-
- RgnHandle contRgn = NewRgn();
- RgnHandle structRgn = NewRgn();
-
- // these Copland accessor functions make life so much nicer...
-
- GetWindowContentRgn( theWindow, contRgn );
- GetWindowStructureRgn( theWindow, structRgn );
-
- // get the old bounds
-
- oldBounds = (*structRgn)->rgnBBox;
-
- // and if the old and new bounds aren't the same, let's set it
-
- if (!EqualRect(&oldBounds, &newBounds)) {
-
- // do some calculations
-
- topInset = (*contRgn)->rgnBBox.top - (*structRgn)->rgnBBox.top;
- leftInset = (*contRgn)->rgnBBox.left - (*structRgn)->rgnBBox.left;
- bottomInset = (*structRgn)->rgnBBox.bottom - (*contRgn)->rgnBBox.bottom;
- rightInset = (*structRgn)->rgnBBox.right - (*contRgn)->rgnBBox.right;
-
- top = newBounds.top + topInset;
- left = newBounds.left + leftInset;
- height = newBounds.bottom - top - bottomInset;
- width = newBounds.right - left - rightInset;
-
- // hide the window (so the user doesn't have to see our manipulations
-
- HideWindow(theWindow);
-
- // move the window to it's new location
-
- MoveWindow(theWindow, left, top, false);
-
- // resize it to its new size
-
- SizeWindow(theWindow, width, height, true);
-
- // select it
-
- SelectWindow(theWindow);
-
- // and show it again all new and happy
-
- ShowWindow(theWindow);
- }
-
- // of course, don't forget to dispose of your regions after you're done using
- // them, else risk memory leaks!
-
- if ( contRgn != nil )
- DisposeRgn( contRgn );
- if ( structRgn != nil )
- DisposeRgn( structRgn );
-
- return;
- }
-
-
- /*******************************************************************************
-
- GetWindowContentRect
-
- Given a window pointer, return the global rectangle that encloses the
- content area of the window.
-
- *******************************************************************************/
- Rect HsoiGetWindowContentRect(WindowRef window)
- {
- WindowRef oldPort;
- Rect contentRect;
-
- GetPort(&oldPort);
- SetPortWindowPort(window);
-
- // it's a nice thing that a window's portRect and contentRect's are the
- // same things.
- contentRect = GetWindowPort(window)->portRect;
- HsoiLocalToGlobalRect(&contentRect);
- SetPort(oldPort);
- return contentRect;
- }
-
-
- /*******************************************************************************
-
- GetWindowStructureRect
-
- This procedure is used to get the rectangle that surrounds the entire
- structure of a window. This works whether or not the window is visible. If
- the window is visible, it is a simple matter of using the bounding
- rectangle of the structure region. If the window is invisible, the
- strucRgn is not valid. To make it valid, the window has to be moved way
- off the screen and then made visible. This generates a valid strucRgn,
- although it is valid for the position that is way off the screen. It still
- needs to be offset back into the original position. Once the bounding
- rectangle for the strucRgn is obtained, the window can then be hidden
- again and moved back to its correct location. Note that ShowHide is used,
- instead of ShowWindow and HideWindow. HideWindow can change the plane of
- the window. Also, ShowHide does not affect the hiliting of windows.
-
- *******************************************************************************/
- Rect HsoiGetWindowStructureRect(WindowRef window)
- {
- GrafPtr oldPort;
- Rect structureRect;
- Point windowLoc;
- RgnHandle structRgn = NewRgn();
-
- if ( IsWindowVisible( window ) )
- {
- // if it's visible, just get it
-
- GetWindowStructureRgn( window, structRgn );
- structureRect = (*structRgn)->rgnBBox;
- }
- else
- {
- // it's not visible, so let's move it offscreen to get it
-
- GetPort(&oldPort);
- SetPortWindowPort(window);
- windowLoc = HsoiGetGlobalTopLeft(window);
-
- // move it offscreen...way off screen
-
- MoveWindow(window, windowLoc.h, kOffscreenLocation, false);
-
- // show the window. look up ShowHide and see why it's nice to use in this context
-
- ShowHide(window, true);
-
- // get the struct rect
-
- GetWindowStructureRgn( window, structRgn );
- structureRect = (*structRgn)->rgnBBox;
-
- // hide the window again
-
- ShowHide(window, false);
-
- // move it back onscreen
-
- MoveWindow(window, windowLoc.h, windowLoc.v, false);
- OffsetRect(&structureRect, 0, windowLoc.v - kOffscreenLocation);
- SetPort(oldPort);
- }
-
- // and dispose of our region for no memory leaks.
-
- if ( structRgn != nil )
- DisposeRgn( structRgn );
-
- return structureRect;
- }
-
-
- /*******************************************************************************
-
- GetWindowDeviceRectNMB (No Menu Bar)
-
- Given a window pointer, find the device that contains most of the window
- and return that device’s bounding rectangle. If this device is the main
- device, remove the menubar area from the rectangle.
-
- *******************************************************************************/
- Rect HsoiGetWindowDeviceRectNMB(WindowRef window)
- {
- Rect deviceRect, tempRect;
-
- deviceRect = HsoiGetWindowDeviceRect(window);
- tempRect = HsoiGetMainScreenRect();
- if (EqualRect(&deviceRect, &tempRect))
- deviceRect.top += LMGetMBarHeight();
-
- return deviceRect;
- }
-
-
- /*******************************************************************************
-
- GetWindowDeviceRect
-
- Given a window pointer, find the device that contains most of the window
- and return that device’s bounding rectangle.
-
- *******************************************************************************/
- Rect HsoiGetWindowDeviceRect(WindowRef window)
- {
- if (gHasColorQD)
- return (*HsoiGetWindowDevice(window))->gdRect;
- else
- return HsoiGetMainScreenRect();
- }
-
-
- /*******************************************************************************
-
- GetWindowDevice
-
- Given a window pointer, find the device that contains most of the window
- and return its handle.
-
- *******************************************************************************/
- GDHandle HsoiGetWindowDevice(WindowRef window)
- {
- return HsoiGetRectDevice(HsoiGetWindowStructureRect(window));
- }
-
-
- #pragma mark -
- #pragma mark ••• Other Utilities •••
-
- /*******************************************************************************
-
- GetRectDevice
-
- Given a rectangle in global coordinates, find the monitor it overlaps the
- most. This is done by iterating over all of the monitors with the
- GetDeviceList and GetNextDevice calls. For each device, we find the area
- of overlap with the given rectangle. The device that results in the
- greatest overlap is returned to the caller.
-
- This routine assumes the existence of the Graphics Device Manager.
-
- *******************************************************************************/
- GDHandle HsoiGetRectDevice(Rect globalRect)
- {
- long area;
- long maxArea;
- GDHandle device;
- GDHandle deviceToReturn;
- Rect intersection;
-
- deviceToReturn = GetMainDevice(); /* Use as default choice. */
- maxArea = 0;
-
- for (device = GetDeviceList(); device != nil; device = GetNextDevice(device)) {
- if (TestDeviceAttribute(device, screenDevice)
- && TestDeviceAttribute(device, screenActive)
- && SectRect(&globalRect, &((*device)->gdRect), &intersection)) {
- area = (intersection.right - intersection.left) *
- (intersection.bottom - intersection.top);
- if (area > maxArea) {
- deviceToReturn = device;
- maxArea = area;
- }
- }
- }
- return deviceToReturn;
- }
-
-
- /*******************************************************************************
-
- LocalToGlobalRect
-
- Convert a rectangle from local coordinates to global coordinates. Like
- QuickDraw’s LocalToGlobal, it assumes that the current port is set
- correctly.
-
- *******************************************************************************/
- void HsoiLocalToGlobalRect(Rect *aRect)
- {
- LocalToGlobal(&topLeft(*aRect));
- LocalToGlobal(&botRight(*aRect));
-
- return;
- }
-
-
- /*******************************************************************************
-
- GetGlobalTopLeft
-
- Return the top left point of the given window’s port in global
- coordinates. This returns the top left point of the window’s content area
- only; it doesn’t include the window’s drag region (or title bar).
-
- *******************************************************************************/
- Point HsoiGetGlobalTopLeft(WindowRef window)
- {
- GrafPtr oldPort;
- Point globalPt;
-
- GetPort(&oldPort);
- SetPortWindowPort(window);
- globalPt = topLeft(GetWindowPort(window)->portRect);
- LocalToGlobal(&globalPt);
- SetPort(oldPort);
- return globalPt;
- }
-
-
- /*******************************************************************************
-
- GetMainScreenRect
-
- Gets the bounding rectangle of the main screen (the one with the menu bar
- on it). This rectangle includes the area that contains the menubar. For
- example, on a Mac Classic, this routine should return (0, 0, 512, 342).
-
- *******************************************************************************/
- Rect HsoiGetMainScreenRect(void)
- {
- GDHandle mainDevice;
- GrafPtr mainPort;
-
- if (gHasColorQD) {
- mainDevice = GetMainDevice();
- return (*mainDevice)->gdRect;
- } else {
- GetWMgrPort(&mainPort);
- return mainPort->portRect;
- }
- }
-
-
-
-
- // this gets our UPP's initialzed...called at startup time
-
- void HsoiInitMenuWindowUPPs( void )
- {
- gTileSetupUPP = NewHsoiSetUpProc( HsoiTileSetup );
- gTileTheWindowUPP = NewHsoiWindowActionProc( HsoiTileTheWindow );
- gStackSetupUPP = NewHsoiSetUpProc( HsoiStackSetup );
- gStackTheWindowUPP = NewHsoiWindowActionProc( HsoiStackTheWindow );
- gLookForSelectedWindowUPP = NewHsoiWindowActionProc( HsoiLookForSelectedWindow );
- gCountWindowsUPP = NewHsoiWindowActionProc( HsoiCountWindows );
- gLookForPreviousWindowUPP = NewHsoiWindowActionProc( HsoiLookForPreviousWindow );
- gCountSomeWindowUPP = NewHsoiWindowActionProc( HsoiCountSomeWindow );
-
- return;
- }
-
- // do just what the function name says...readjust the window. when the window itself
- // gets resized and moved by the stacking/tiling, we'll have to readjust the
- // "internal" window stuff, like our text area and scrollbars, etc.
-
- void HsoiReadjustAfterStackTile( WindowRef window )
- {
- Rect r;
-
- // the biggie that actually readjusts everything
-
- HsoiViewChanged( window );
-
- // refigure out our text rect
-
- HsoiCalcTextRect( window, &r );
-
- // and then "invalidate" that text rect so the Window Manager issues an
- // update event to redraw that rect's contents
-
- InvalRect( &r );
-
- return;
- }
-
-
- // the following 2 functions are utility functions to allow Knaster/Rollin's technique
- // of maintaining the window list by creating a "linked list" of the windows using
- // the window's refCon field. but since HAS already uses the window's refCon (to store
- // a reference to the window's associated DocumentRecord), we can't use Knaster/Rollin's
- // technique as is. So instead, we've got our own "refCon" (of sorts) in the DocumentRecord
- // (called nextWindow) where we'll store this next window's address. In essence, these
- // functions act the same as GetWRefCon/SetWRefCon (but get/set from the DocumentRecord).
- // if you want to extract this tiling/stacking code from HAS and you won't be using the
- // window's refCon for anything, you could just use the window's refCon to store the
- // linked list data and then throughout this code, just replace HsoiGetNextWindow with
- // GetWRefCon and HsoiSetNextWindow with SetWRefCon.
-
- long HsoiGetNextWindow( WindowRef window )
- {
- // we should check to ensure this is a document window (i.e. a window with a
- // DocumentRecord stored in the refCon), but for now, we'll just assume this
- // is the case
-
- return (*(DocumentHandle)GetWRefCon( window ))->nextWindow;
- }
-
- void HsoiSetNextWindow( WindowRef window, long data )
- {
- // we should check to ensure this is a document window (i.e. a window with a
- // DocumentRecord stored in the refCon), but for now, we'll just assume this
- // is the case
-
- (*(DocumentHandle)GetWRefCon( window ))->nextWindow = data;
-
- return;
- }
-